# -*- coding: utf-8 -*-
import sys
import os
import re
import logging
from  subprocess import PIPE, Popen
from django.template.defaultfilters import slugify
from docs.models import *

class PageIntegrityException(Exception):
    pass

class VersionException(Exception):
    pass

class DocumentIntegrityException(Exception):
    pass

def existsdir(path):
    if os.path.exists(path) and os.path.isdir(path):
        return True
    else:
        return False
    
def open_cmd(*args, **kwargs):
    try:
        logging.debug(" ".join(args)) 
        p = Popen(args, stdout = PIPE, **kwargs)
        r = p.wait()
        if not r == 0:
            logging.debug("run command %s fail." % args[0][0])
        f = p.stdout
        return f
    except OSError, e:
        print e
    

def split_rst_file(path):
    f = open(path)
    content = f.read()
    paragraphs_content = re.split(r"(\n{2,}[^\n]+?\n[-=]+\n{2,})",content)
    paragraphs_length = len(paragraphs_content)
    if paragraphs_length % 2 == 0:
        raise PageIntegrityException, "page %s's paragraphs number %s is a even number, it should never be." % (path, paragraphs_length)
    result = []
    for i, paragraph_content in enumerate(paragraphs_content):
        if i == 0:
            result.append(paragraph_content.strip())
        elif i % 2 == 1:
            original_title = paragraph_content
            continue
        elif i % 2 == 0:
            result.append((original_title + paragraph_content).strip())
    return result



def update_docs_in_db(project, docs_files, to_version):
    from_version = project.current_version
    if from_version:
        try:
            from_version_object = Version.objects.get(project = project, revision = from_version)
        except Version.DoesNotExist:
            raise VersionException, "proejct %s version %s didn't exist in database" % (project, from_version)
    else:
        from_version_object = None
        
    changed = len(docs_files) != 0
    log = "\n".join(["%s %s" % (s,f) for f,s,p in docs_files])
    to_version_object = Version(project = project, last_version = from_version_object, revision = to_version, changed = changed, log = log)
    to_version_object.save()
    
    logging.info("svn synchronized %s doc files, added %s , updated %s , deleted %s " % (
                                                                                      len(docs_files),
                                                                                      len([f for f,s,p in docs_files if s == "A"]),
                                                                                      len([f for f,s,p in docs_files if s == "U"]),
                                                                                      len([f for f,s,p in docs_files if s == "D"]),
                                                                                      ))
    before_pages_count = project.effective_pages.count()
    before_paragraphs_count = project.effective_paragraphs.count()
    before_translated_paragraphs_count = project.translated_paragraphs.count()
    logging.info("before synchronize database, %s effective pages, %s effective paragraphs(%s translated)" % (before_pages_count,
                                                                                                              before_paragraphs_count,
                                                                                                              before_translated_paragraphs_count,
                                                                                                              
                                                                                                              ))
    
    for path, status, splited_paragraphs in docs_files:
        logging.info("\n\n%s       : %s" % (status, path))
        slug = path.replace(os.sep, '-')
        if status == "A" or status == "U":
            page, page_created = Page.objects.get_or_create(project = project, path = path, defaults={'added_version': to_version,
                                                                                                      'current_version': to_version,
                                                                                                      'slug': slug,
                                                                                                      })
            
            page_change = PageChange(version = to_version_object, page = page)
            page_change.save()
            if page_created:
                logging.info("created : %s" % path)
            else:
                logging.info("found   : %s" % path)
                if status == "A":
                    logging.error('%s not created, but status is "A", maybe a file restored' % path)
                    page.suspended = True
                    page.added_version = to_version
            
            paragraphs_count = page.effective_paragraphs.count()
            parsed_paragraphs_count = len(splited_paragraphs)
            logging.info("before synchronize, %s effective paragraphs in database" % paragraphs_count)
            logging.info("before synchronize, %s paragraphs parsed" % parsed_paragraphs_count)
            existed_paragraph = []
            created_paragraph = []
            for ordinal, original in enumerate(splited_paragraphs):
                title = original.split("\n")[0].strip()
                paragraph, paragraph_created = Paragraph.objects.get_or_create(page = page, original = original,
                                                                               title = title, current_version__lt = to_version,
                                                                               ordinal__range =(ordinal-2, ordinal +2),
                                                                               defaults={'ordinal': ordinal,
                                                                                         'added_version': to_version,
                                                                                         'current_version': to_version,
                                                                                        })
                if paragraph_created:
                    created_paragraph.append(paragraph)
                    if from_version:
                        logging.info("created paragraph [%s], ordinal: %s, id: %s" % (paragraph.title, ordinal,paragraph.id))
                        page_change.paragraphs.add(paragraph)
                        try:
                            history = Paragraph.objects.filter(page = page, title = title, 
                                                                 current_version__lt = to_version,
                                                                 modified_paragraphs__isnull = True,
                                                                 ordinal__range =(ordinal-2, ordinal +2)).latest('created')
                            logging.info("found history paragraph, id: %s " % history.id)
                            paragraph.history = history
                            if history.translation:
                                history_translation = history.translation
                                logging.info("found history translation, id: %s " % history_translation.id)
                                translation = history_translation
                                translation.paragraph = paragraph
                                translation.inherited_from = history_translation
                                translation.id = None
                                translation.save(force_insert = True)
                                history_translation = history.translation
                                logging.info("use history translation, new translation id: %s , old translation id: %s" % (translation.id,
                                                                                                                           history_translation.id,
                                                                                                                           ))
                                
                            else:
                                translation = None
                            paragraph.translation = translation
                        except Paragraph.DoesNotExist:
                            pass
                else:
                    existed_paragraph.append(paragraph)
                    paragraph.current_version = to_version
                    paragraph.ordinal = ordinal
                paragraph.save()
            logging.info("after synchronize, %s paragraphs created, %s paragraphs existed" % (len(created_paragraph),
                                                                                              len(existed_paragraph),
                                                                                              ))
            logging.info("created paragraphs:\n" + "    ".join((str(p.ordinal) for p in created_paragraph)))
            logging.info("existed paragraphs:\n" + "    ".join((str(p.ordinal) for p in existed_paragraph)))
            assert len(set(created_paragraph) | set(existed_paragraph)) == parsed_paragraphs_count
            assert len(set(created_paragraph) & set(existed_paragraph)) == 0
            page.current_version = to_version
            page.save()
            page_change.save()
            logging.info("check page integrity for page %s" % path)
            new_paragraphs_count = page.effective_paragraphs.count()
            assert new_paragraphs_count == parsed_paragraphs_count
            page_integrity = page.is_integrity
            if isinstance(page_integrity, int) and page_integrity != -1:
                raise PageIntegrityException, "page %s not integrity, paragraph %s have some problem" % (page.path, page_integrity)
            
            if isinstance(page_integrity, set) and len(page_integrity) != 0:
                raise PageIntegrityException, "page %s not integrity, lines below have some problem:\n%s" % (page.path, "\n".join(page_integrity))
            
            
        elif status == "D":
            page, page_created = Page.objects.get_or_create(project = project, path = path, defaults={'added_version': to_version,
                                                                                                       'current_version': to_version,
                                                                                                       'slug': slug,
                                                                                                       })
            page_change = PageChange(version = to_version_object, page = page)
            page_change.save()
            if page_created:
                raise PageIntegrityException, "page %s didn't exist before, but status is 'D' " % path
            page.current_version = to_version
            page.suspended = True
            page.save()
    
    
    un_touched_pages = Page.objects.filter(project = project, current_version = from_version, suspended = False)
    logging.info("after synchronize parsed pages, %s untouched pages" % un_touched_pages.count())
    for page in un_touched_pages:
        for paragraph in page.effective_paragraphs.all():
            paragraph.current_version = to_version
            paragraph.save()
        page.current_version = to_version
        page.save()
    if not from_version_object: project.added_version = to_version
    project.current_version = to_version
    project.save()
    
    atfer_effective_pages = set(project.effective_pages.values_list('path', flat=True))
    atfer_paragraphs_count = project.effective_paragraphs.count()
    atfer_translated_paragraphs_count = project.translated_paragraphs.count()
    logging.info("after  synchronize database, %s effective pages, %s effective paragraphs(%s translated)" % (len(atfer_effective_pages),
                                                                                                              atfer_paragraphs_count,
                                                                                                              atfer_translated_paragraphs_count,
                                                                                                              ))
    
    original_docs_dir_files = project.doc_files_set
    if atfer_effective_pages != original_docs_dir_files:
        diff = atfer_effective_pages ^ original_docs_dir_files
        raise DocumentIntegrityException, "document not synchronized in database and docs dir:\n%s" % "\n".join(diff)
    
    logging.info("checked project files integrity")